#include "StdAfx.h"
#include ".\connection.h"

namespace HTTP {

#ifdef _USE_LOGGING
DEFINE_LOGGER(Connection)
#endif

Connection::Connection()
	: m_context(::GetTickCount())
{
}

Connection::~Connection()
{
	Close();
}

bool Connection::Connect(const CString &strUrl, LPCTSTR pszMethod)
{
	LOG("URL: ", strUrl);

    if (!m_internet.Open(HTTP_USER_AGENT))
	{
		LOG("Cannot open internet connection");
		return false;
	}

	CUrl url;
	if (!url.CrackUrl(strUrl))
	{
		LOG("Invalid URL", strUrl);
		return false;
	}

	if (!m_connection.Connect(m_internet, url.GetHostName(), url.GetPortNumber(), INTERNET_SERVICE_HTTP, 0, (DWORD_PTR)&m_context))
	{
		LOG("Cannot connect to server");
		return false;
	}

	CString path(url.GetUrlPath());
	if (url.GetExtraInfoLength())
		path += url.GetExtraInfo();

	if (!m_request.Open(m_connection, pszMethod, path, INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_PRAGMA_NOCACHE | INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE,(DWORD_PTR)&m_context))
	{
		LOG("Cannot open HTTP connection");
		return false;
	}

	return true;
}

BOOL Connection::operator ! () const
{
	return m_request.operator ! ();
}

bool Connection::Open(const CString &strUrl)
{
	if (!Connect(strUrl, METHOD_GET))
		return false;

	if (!m_request.SendRequest())
	{
		// problem while send request
		LOG("Cannot open GET stream");
		return false;
	}

	return true;
}

bool Connection::Open(const CString &strUrl, const CString &headers)
{
    if (headers.IsEmpty())
        return Open(strUrl);

	if (!Connect(strUrl, METHOD_GET))
		return false;

	if (!m_request.SendRequest(headers, (DWORD)headers.GetLength()))
	{
		// problem while send request
		LOG("Cannot open GET stream");
		return false;
	}

	return true;
}

bool Connection::Send(const CString &strUrl, const CString &headers, const CStringA &body)
{
	if (!Connect(strUrl, METHOD_POST))
		return false;

	if (!m_request.SendRequest(headers, (DWORD)headers.GetLength(), (LPVOID)(LPCSTR)body, body.GetLength()))
	{
		// problem while send request
		LOG("Cannot send request");
		return false;
	}

	return true;
}

DWORD Connection::GetStatusCode()
{
	DWORD dwCode = 0, dwBufSize = sizeof(DWORD);
	if (!m_request.QueryInfo(HTTP_QUERY_STATUS_CODE|HTTP_QUERY_FLAG_NUMBER, &dwCode, &dwBufSize))
        return 0;

    return dwCode;
}

CString Connection::GetLastModifiedHeader()
{
    CString strBuffer;

    DWORD dwBufSize = 64*sizeof(TCHAR);
	if (!m_request.QueryInfo(HTTP_QUERY_LAST_MODIFIED, strBuffer.GetBuffer(64), &dwBufSize))
        strBuffer.ReleaseBuffer(0);
    else
        strBuffer.ReleaseBuffer(dwBufSize/sizeof(TCHAR));

    return strBuffer;
}

bool Connection::GetResponse(CStringA& response)
{
	DWORD dwCode = GetStatusCode();
	if (!(dwCode >= HTTP_STATUS_OK && dwCode < 300))
	{
		// problem while send request
		LOG("Invalid status: %i", dwCode);
		return false;
	}

	response.Empty();

	// read response portion by portion
	const DWORD portion = 1024;
	char szBuffer[portion+1];
	while (true)
	{
		ZeroMemory(szBuffer, sizeof(szBuffer));

		DWORD bytesread = 0;
		if (!m_request.Read(szBuffer, portion, &bytesread))
		{
			// problem while reading response
			LOG("Cannot read data");
			return false;
		}

		if (0 == bytesread) // response was completely read
			break;

		szBuffer[bytesread] = 0;
		response += szBuffer;
	}

	LOG("Response: ", response);

	return true;
}

void Connection::Close()
{ 
	m_request.Close();
	m_connection.Close();
	m_internet.Close();
}

} // namespace HTTP
